package jfilters.libcolor;

/*
    Copyright Janne Liljeblad 2008.

    This file is part of JFilters.

    JFilters is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    JFilters is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with JFilters.  If not, see <http://www.gnu.org/licenses/>.
*/

import jfilters.libmath.JFMath;
import jfilters.filters.JFilter;
import java.awt.image.*;

//--- Allmethod names correspont with gimp_histogram_ removed from beginning.
public class JFHistogram 
{

	public static final int CHANNEL_VALUE = 0;  /*< desc="Value" >*/
	public static final int CHANNEL_RED   = 1;  /*< desc="Red"   >*/
	public static final int CHANNEL_GREEN = 2;  /*< desc="Green" >*/
	public static final int CHANNEL_BLUE  = 3;  /*< desc="Blue"  >*/
	public static final int CHANNEL_ALPHA = 4;  /*< desc="Alpha" >*/
	public static final int CHANNEL_RGB   = 5;  /*< desc="RGB", pdb-skip >*/

	//--- for bit wise
	public static final int alpha = 24;
	public static final int red = 16;
	public static final int green = 8;

  	public double[][] values = null;
  	public int n_channels = 0;
	public BufferedImage img;

	public JFHistogram( BufferedImage img )
	{
		this( img, img.getColorModel().getNumComponents() + 1 );// CHANNEL_VALUE ?!?
	}

	public JFHistogram( BufferedImage img, int n_channels )
	{
		this.img = img;
		alloc_values( n_channels );
	}

	//--- After killin gimp specifics, this was left.
	public void calculate()
	{
		calculate_sub_region();
	}

	private void alloc_values ( int channels )
	{
		this.n_channels = channels;
		values = new double[ channels ][ 256 ];
		//--- java sets these to 0.0 auto magigally.  (?)
	}
	public double get_maximum( int  channel )
	{
		double max = 0.0;
		int    x;
		
		if (channel == CHANNEL_RGB )
			for (x = 0; x < 256; x++)
			{
				max = JFMath.MAX (max, values[ CHANNEL_RED ][x]);
				max = JFMath.MAX (max, values[ CHANNEL_BLUE ][x]);
				max = JFMath.MAX (max, values[ CHANNEL_GREEN ][x]);
			}
		else
			for (x = 0; x < 256; x++)
				if ( values[channel][x] > max) max = values[channel][x];
		
		return max;
	}

	public double get_value( int channel, int bin )
	{
		if (channel == CHANNEL_RGB )
		{
			double min = values[ CHANNEL_RED ][bin];
			min = JFMath.MIN (min, values[ CHANNEL_GREEN ][bin]);
			return JFMath.MIN (min, values[ CHANNEL_BLUE ][bin]);
		}
		else return values[channel][bin];
	}

	//--- int imgchannel as defined by JFilter.RED, JFilter.GREEN etc...
	public double get_channel ( int imgchannel, int bin )
	{
		return get_value ( imgchannel + 1, bin);
	}
	//--- ?, CHANNEL_VALUE , meaning image channels?
	public int n_channels(){ return n_channels - 1; }

	public double get_count( int channel, int start, int end )
	{
		if (channel == CHANNEL_RGB)
		{
			return ( get_count( CHANNEL_RED, start, end )   +
				get_count ( CHANNEL_GREEN, start, end ) +
				get_count ( CHANNEL_BLUE, start, end ));
		}
		
		start = JFMath.CLAMP (start, 0, 255);
		end = JFMath.CLAMP (end, 0, 255);
		
		double count = 0.0;
		for ( int i = start; i <= end; i++)
			count += values[ channel ][ i ];
		
		return count;
	}

	public double get_mean( int channel, int start, int end )
	{
		int    i;
		double mean = 0.0;
		double count;
		
		start = JFMath.CLAMP (start, 0, 255);
		end = JFMath.CLAMP (end, 0, 255);
	
		if (channel == CHANNEL_RGB )
		{
			for (i = start; i <= end; i++)
			{
				mean += (	i * values[ CHANNEL_RED ][i]   +
						i * values[ CHANNEL_GREEN ][i] +
						i * values[ CHANNEL_BLUE ][i] );
			}
		}
		else
		{
			for ( i = start; i <= end; i++ )
				mean += i * values[channel][i];
		}
		
		count = get_count ( channel, start, end );
		
		if (count > 0.0)
			return mean / count;
		
			return mean;
	}

	public int get_median( int channel, int start, int end )
	{
		int    i;
		double sum = 0.0;
		double count;
	
		start = JFMath.CLAMP (start, 0, 255);
		end = JFMath.CLAMP (end, 0, 255);
		
		count = get_count ( channel, start, end);
		
		if (channel == CHANNEL_RGB )
		{
			for (i = start; i <= end; i++)
			{
				sum += ( 	values[CHANNEL_RED][i]   +
						values[CHANNEL_GREEN][i] +
						values[CHANNEL_BLUE][i] );
			
				if (sum * 2 > count)
				return i;
			}
		}
		else
		{
			for ( i = start; i <= end; i++ )
			{
				sum += values[channel][i];
			
				if (sum * 2 > count)
				return i;
			}
		}
		return -1;//this has to mean error
	}

	public double get_std_dev( int channel, int start, int end )
	{
		int    i;
		double dev = 0.0;
		double count;
		double mean;
	
		mean  = get_mean  ( channel, start, end);
		count = get_count ( channel, start, end);
	
		if (count == 0.0)
			count = 1.0;
	
		for (i = start; i <= end; i++)
			dev += get_value( channel, i ) * (i - mean) * (i - mean);
	
		return Math.sqrt( dev / count );
	}

	private void calculate_sub_region()
	{
		int[] src = JFilter.getBank( img );

		int sr, sg, sb, sa, max;

		for( int i = 0; i < src.length; i++ )
		{
			sa = ( src[ i ] >> alpha ) & 0xff;
			sr = ( src[ i ] >> red ) & 0xff;
			sg = ( src[ i ] >> green ) & 0xff;
			sb = src[ i ] & 0xff;

			values[ CHANNEL_RED ][sr] += 1.0;
			values[ CHANNEL_GREEN ][sg] += 1.0;
			values[ CHANNEL_BLUE ][sb] += 1.0;
			values[ CHANNEL_ALPHA ][sa] += 1.0;
			max = ( sr > sg ) ? sr : sg;
			
			if (sb > max)
				values[ CHANNEL_VALUE ][ sb ] += 1.0;
			else
				values[ CHANNEL_VALUE ][ max ] += 1.0;
		}
	}

}//end clas
